package com.jlf.core.engine.builder.db;

import com.jlf.core.engine.builder.AutoBuilderTool;
import com.jlf.core.engine.constant.SchemaConstant;
import com.jlf.core.engine.domain.SchemaDomainDO;
import com.jlf.core.engine.domain.SchemaFieldDO;
import com.jlf.core.engine.domain.SchemaIndexDO;
import org.apache.commons.collections4.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * postgresql的建表构造器
 *
 * @author wujr
 * 2023/7/14
 * @version 1.0
 * 【变更历史】
 * [wujr 2023/7/14 1.0 新增]
 */
public class AutoPgSqlTableBuilder {
    private static final ConcurrentHashMap<Integer, String> _jdbcTypes = new ConcurrentHashMap<>();
    private static final List<SchemaFieldDO> _commonFields = new ArrayList<>();

    static{
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.SHORT, "status", SchemaConstant.Jdbc.Type.SHORT, "status", 0, "1"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.SHORT, "priority", SchemaConstant.Jdbc.Type.SHORT, "priority", 0, "1"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.STRING, "createAppCode", SchemaConstant.Jdbc.Type.STRING, "create_app_code", 32, "unknown"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.LONG, "createProfileId", SchemaConstant.Jdbc.Type.BIGINT, "create_profile_id", 0, "-1"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.LONG, "createIdentityId", SchemaConstant.Jdbc.Type.BIGINT, "create_identity_id", 0, "-1"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.STRING, "createUsername", SchemaConstant.Jdbc.Type.STRING, "create_username", 128, "unknown"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.LOCAL_DATETIME, "createTime", SchemaConstant.Jdbc.Type.TIMESTAMP, "create_time", 0, "now()"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.STRING, "updateAppCode", SchemaConstant.Jdbc.Type.STRING, "update_app_code", 32, "unknown"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.LONG, "updateProfileId", SchemaConstant.Jdbc.Type.BIGINT, "update_profile_id", 0, "-1"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.LONG, "updateIdentityId", SchemaConstant.Jdbc.Type.BIGINT, "update_identity_id", 0, "-1"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.STRING, "updateUsername", SchemaConstant.Jdbc.Type.STRING, "update_username", 128, "unknown"));
        _commonFields.add(new SchemaFieldDO(SchemaConstant.Java.Type.LOCAL_DATETIME, "updateTime", SchemaConstant.Jdbc.Type.TIMESTAMP, "update_time", 0, "now()"));
    }
    static{
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.SHORT, "int2");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.INTEGER, "int4");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.BIGINT, "int8");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.FLOAT, "float4");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.DOUBLE, "float8");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.DECIMAL, "numeric");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.BOOLEAN, "bool");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.STRING, "varchar");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.BLOB, "bytea");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.CLOB, "text");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.DATE, "date");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.TIME, "time");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.TIMESTAMP, "timestamp");
        _jdbcTypes.put(SchemaConstant.Jdbc.Type.DATETIME, "timestamp with time zone");
    }
    public static List<String> generate(SchemaDomainDO schemaDomain,
                                        List<SchemaFieldDO> schemaFields,
                                        List<SchemaIndexDO> schemaIndexes){
        List<String> sqles = new ArrayList<>();
        StringBuilder sbTable= new StringBuilder("create table if not exists ")
                .append(schemaDomain.getDomainTable()).append("(");

        // 添加主键id
        sbTable.append("id int8 not null");

        for (SchemaFieldDO schemaField : schemaFields){
            sbTable.append(",").append(buildColumnSql(schemaField));
        }
        if (schemaDomain.isLockOptimistic()){
            // 乐观锁字段
            sbTable.append(",").append(buildColumnSql(AutoBuilderTool._optimisticVersionField));
        }        // 添加公共字段
        for (SchemaFieldDO schemaField : _commonFields){
            sbTable.append(",").append(buildColumnSql(schemaField));
        }

        sbTable.append(");");
        sqles.add(sbTable.toString());
        // 添加主键、索引、唯一性约束
        if (schemaDomain.isAutoIncrement()){
            // 创建序号
            String seq = "create sequence seq_" + schemaDomain.getDomainCode() +
                    " start with 1 increment by 1 minvalue 1 no maxvalue cache 1 owned by " +
                    schemaDomain.getDomainTable() + ".id;";
            sqles.add(seq);
        }
        String pk = "alter table " + schemaDomain.getDomainTable() + " add constraint pk_" + schemaDomain.getDomainTable() + "_key primary key(id);";
        sqles.add(pk);
        if (!CollectionUtils.isEmpty(schemaIndexes)){
            for (SchemaIndexDO schemaIndex : schemaIndexes){
                if (schemaIndex.isIndex()) {
                    String sqlIndex = buildIndexSql(schemaDomain, schemaIndex);
                    sqles.add(sqlIndex);
                }else if (schemaIndex.isUnique()){
                    String sqlUnique = buildUniqueSql(schemaDomain, schemaIndex);
                    sqles.add(sqlUnique);
                }
            }
        }

        return sqles;
    }
    private static String buildUniqueSql(SchemaDomainDO schemaDomain, SchemaIndexDO schemaIndex){
        return "alter table " + schemaDomain.getDomainTable() + " ADD CONSTRAINT " + schemaIndex.getName() + " UNIQUE (" + schemaIndex.getColumns() + ")";
    }
    private static String buildIndexSql(SchemaDomainDO schemaDomain, SchemaIndexDO schemaIndex){
        StringBuilder sb = new StringBuilder("create ");
        if (schemaIndex.isUniqueness()){
            sb.append("UNIQUE ");
        }
        sb.append("index ").append(schemaIndex.getName())
                .append(" on ").append(schemaDomain.getDomainTable())
                .append("(").append(schemaIndex.getColumns()).append(");");

        return sb.toString();
    }
    private static String buildColumnSql(SchemaFieldDO schemaField){
        StringBuilder sb = new StringBuilder(schemaField.getJdbcFieldCode())
                .append(" ").append(_jdbcTypes.get(schemaField.getJdbcFieldType()));
        if (schemaField.isString()){
            // 字符串
            sb.append("(").append(schemaField.getJdbcFieldLength()).append(")");
        }
        if (!schemaField.isNullable()){
            // 该字段不能为空
            sb.append(" not null");
        }
        if (schemaField.hasDefaultValue()){
            // 有默认值
            sb.append(" default ");
            if (schemaField.isString()){
                // 字符串
                sb.append("'").append(schemaField.getDefaultValue()).append("'");
            }else{
                sb.append(schemaField.getDefaultValue());
            }
        }
        return sb.toString();
    }
}
